// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Author: wsfuyibing <682805@qq.com>
// Date: 2025-02-27

package delivery

import (
	"context"
	"encoding/json"
	"fmt"
	"github.com/valyala/fasthttp"
	"integration/app/lib/conf"
	"integration/app/lib/nod/crmeb"
	"integration/app/lib/wechat"
	"integration/app/models"
	"net/http"
	"strconv"
	"time"
)

var (
	Uploader UploaderManager
)

type (
	// UploaderManager
	// 上传发货状态.
	UploaderManager interface {
		Upload(ctx context.Context, routine *conf.ErpRoutine, order *models.Order, carts []*models.OrderCartInfo, openId string) (ur *UploaderResult, err error)
	}

	UploaderResult struct {
		OpenId string `json:"openid"`
	}

	uploader struct {
	}
)

func (o *uploader) Upload(ctx context.Context, routine *conf.ErpRoutine, order *models.Order, carts []*models.OrderCartInfo, openId string) (ur *UploaderResult, err error) {
	var (
		info    crmeb.CartInfo
		sku     int
		quality float64
		payload []byte
	)

	// 1. 商品计数.
	for _, cart := range carts {
		if info, err = crmeb.NewCartInfo(cart.CartInfo); err != nil {
			continue
		}
		if ff, fe := strconv.ParseFloat(fmt.Sprintf(`%v`, info.Quality), 64); fe == nil {
			sku++
			quality += ff
		}
	}

	// 2. 生成入参.
	if payload, err = json.Marshal(map[string]any{
		"order_key": map[string]any{
			"order_number_type": 2,
			"transaction_id":    order.TradeNo,
		},
		"logistics_type": "2",
		"delivery_mode":  "1",
		"shipping_list": []map[string]any{
			{
				"item_desc": fmt.Sprintf(`共%v类%v件商品, 详见出库单`, sku, quality),
			},
		},
		"upload_time": time.Now().Format("2006-01-02T15:04:05.999Z07:00"),
		"payer": map[string]any{
			"openid": openId,
		},
	}); err != nil {
		return
	}

	if err = o.send(ctx, routine, payload, false); err == nil {
		ur = &UploaderResult{
			OpenId: openId,
		}
	}
	return
}

func (o *uploader) init() *uploader {
	return o
}

func (o *uploader) send(ctx context.Context, routine *conf.ErpRoutine, payload []byte, refresh bool) (err error) {
	var (
		request  = fasthttp.AcquireRequest()
		response = fasthttp.AcquireResponse()
		result   = &struct {
			ErrCode any    `json:"errcode"`
			ErrMsg  string `json:"errmsg"`
		}{}
		retry = false
		token string
	)

	// 1. 监听结束.
	defer func() {
		fasthttp.ReleaseRequest(request)
		fasthttp.ReleaseResponse(response)

		// 1.2 重试.
		if retry {
			err = o.send(ctx, routine, payload, true)
		}
	}()

	// 1. 获取令牌.
	if refresh {
		token, err = wechat.AccessToken.RefreshToken(ctx, routine)
	} else {
		token, err = wechat.AccessToken.GetToken(ctx, routine)
	}

	// 2. 准备请求.
	request.SetBody(payload)
	request.SetRequestURI(fmt.Sprintf(`https://api.weixin.qq.com/wxa/sec/order/upload_shipping_info?access_token=%s`, token))
	request.Header.SetMethod(http.MethodPost)
	request.Header.SetContentType("application/json;charset=utf-8")

	// 3. 请求过程.
	if err = fasthttp.Do(request, response); err != nil {
		return
	}
	if status := response.StatusCode(); status != http.StatusOK {
		err = fmt.Errorf(`HTTP %d %s`, status, http.StatusText(status))
		return
	}

	// 4. 解析结果.
	if err = json.Unmarshal(response.Body(), result); err != nil {
		return
	}

	// 4.1 解析失败.
	//     若返回编码为40001允许重试, 防止令牌过期.
	if code := fmt.Sprintf(`%v`, result.ErrCode); code != "0" {
		if code != "40001" || !refresh {
			retry = true
			return
		}

		err = fmt.Errorf(`%v: %s`, result.ErrCode, result.ErrMsg)
		return
	}
	return
}
